import java.util.*;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

class Transaction {
    private LocalDate date;
    private double amount;
    private String category;
    private String description;

    public Transaction(LocalDate date, double amount, String category, String description) {
        this.date = date;
        this.amount = amount;
        this.category = category;
        this.description = description;
    }

    public LocalDate getDate() { return date; }
    public double getAmount() { return amount; }
    public String getCategory() { return category; }

    @Override
    public String toString() {
        return String.format("%s | %.2f | %s | %s", date, amount, category, description);
    }
}

class FinanceManager {
    private List<Transaction> incomes;
    private List<Transaction> expenses;
    private double monthlyBudget;

    public FinanceManager() {
        incomes = new ArrayList<>();
        expenses = new ArrayList<>();
        monthlyBudget = 0;
    }

    public void addIncome(LocalDate date, double amount, String category, String description) {
        if (amount <= 0) {
            throw new IllegalArgumentException("收入金额必须为正数");
        }
        incomes.add(new Transaction(date, amount, category, description));
    }

    public void addExpense(LocalDate date, double amount, String category, String description) {
        if (amount <= 0) {
            throw new IllegalArgumentException("支出金额必须为正数");
        }
        expenses.add(new Transaction(date, amount, category, description));
    }

    public void displayAllTransactions() {
        System.out.println("\n收入记录：");
        for (Transaction income : incomes) {
            System.out.println(income);
        }
        System.out.println("\n支出记录：");
        for (Transaction expense : expenses) {
            System.out.println(expense);
        }
    }

    public void queryTransactions(LocalDate startDate, LocalDate endDate, String category) {
        System.out.println("\n符合条件的收入记录：");
        for (Transaction income : incomes) {
            if (isTransactionMatch(income, startDate, endDate, category)) {
                System.out.println(income);
            }
        }
        System.out.println("\n符合条件的支出记录：");
        for (Transaction expense : expenses) {
            if (isTransactionMatch(expense, startDate, endDate, category)) {
                System.out.println(expense);
            }
        }
    }

    private boolean isTransactionMatch(Transaction transaction, LocalDate startDate, LocalDate endDate, String category) {
        boolean dateMatch = (startDate == null || !transaction.getDate().isBefore(startDate)) &&
                (endDate == null || !transaction.getDate().isAfter(endDate));
        boolean categoryMatch = category == null || category.isEmpty() || transaction.getCategory().equalsIgnoreCase(category);
        return dateMatch && categoryMatch;
    }

    public void setMonthlyBudget(double budget) {
        if (budget < 0) {
            throw new IllegalArgumentException("预算不能为负数");
        }
        this.monthlyBudget = budget;
    }

    public void displayMonthlyStatistics(int year, int month) {
        YearMonth yearMonth = YearMonth.of(year, month);
        LocalDate startOfMonth = yearMonth.atDay(1);
        LocalDate endOfMonth = yearMonth.atEndOfMonth();

        double totalIncome = calculateTotal(incomes, startOfMonth, endOfMonth);
        double totalExpense = calculateTotal(expenses, startOfMonth, endOfMonth);
        double remainingBudget = monthlyBudget - totalExpense;

        System.out.printf("\n%d年%d月统计报告：\n", year, month);
        System.out.printf("总收入：%.2f\n", totalIncome);
        System.out.printf("总支出：%.2f\n", totalExpense);
        System.out.printf("月度预算：%.2f\n", monthlyBudget);
        System.out.printf("剩余预算：%.2f\n", remainingBudget);

        System.out.println("\n各类别支出统计：");
        Map<String, Double> categoryExpenses = calculateCategoryExpenses(expenses, startOfMonth, endOfMonth);
        for (Map.Entry<String, Double> entry : categoryExpenses.entrySet()) {
            System.out.printf("%s: %.2f\n", entry.getKey(), entry.getValue());
        }
    }

    private double calculateTotal(List<Transaction> transactions, LocalDate startDate, LocalDate endDate) {
        return transactions.stream()
                .filter(t -> !t.getDate().isBefore(startDate) && !t.getDate().isAfter(endDate))
                .mapToDouble(Transaction::getAmount)
                .sum();
    }

    private Map<String, Double> calculateCategoryExpenses(List<Transaction> transactions, LocalDate startDate, LocalDate endDate) {
        Map<String, Double> categoryExpenses = new HashMap<>();
        for (Transaction t : transactions) {
            if (!t.getDate().isBefore(startDate) && !t.getDate().isAfter(endDate)) {
                categoryExpenses.merge(t.getCategory(), t.getAmount(), Double::sum);
            }
        }
        return categoryExpenses;
    }
}

public class PersonalFinanaceManager {
    private static final Scanner scanner = new Scanner(System.in);
    private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final FinanceManager financeManager = new FinanceManager();

    public static void main(String[] args) {
        while (true) {
            printMenu();
            int choice = getIntInput("请输入选项序号：");

            switch (choice) {
                case 1:
                    recordIncome();
                    break;
                case 2:
                    recordExpense();
                    break;
                case 3:
                    financeManager.displayAllTransactions();
                    break;
                case 4:
                    queryTransactions();
                    break;
                case 5:
                    setMonthlyBudget();
                    break;
                case 6:
                    displayMonthlyStatistics();
                    break;
                case 7:
                    System.out.println("感谢使用个人账单管理系统。再见！");
                    return;
                default:
                    System.out.println("无效的选择。请重试。");
            }

            System.out.println("\n按回车键返回主菜单...");
            scanner.nextLine();
        }
    }

    private static void printMenu() {
        System.out.println("\n=================================");
        System.out.println("欢迎使用个人账单管理系统");
        System.out.println("=================================");
        System.out.println("1. 记录收入");
        System.out.println("2. 记录支出");
        System.out.println("3. 查看所有账单");
        System.out.println("4. 查询账单");
        System.out.println("5. 设置月度预算");
        System.out.println("6. 查看月度统计报告");
        System.out.println("7. 退出系统");
    }

    private static void recordIncome() {
        System.out.println("\n记录收入：");
        try {
            LocalDate date = getDateInput("日期（YYYY-MM-DD）：");
            double amount = getDoubleInput("金额：");
            String category = getStringInput("类别（如工资、奖金等）：");
            String description = getStringInput("备注：");
            financeManager.addIncome(date, amount, category, description);
            System.out.println("收入已成功记录！");
        } catch (IllegalArgumentException e) {
            System.out.println("错误：" + e.getMessage());
        }
    }

    private static void recordExpense() {
        System.out.println("\n记录支出：");
        try {
            LocalDate date = getDateInput("日期（YYYY-MM-DD）：");
            double amount = getDoubleInput("金额：");
            String category = getStringInput("类别（如餐饮、交通、购物等）：");
            String description = getStringInput("备注：");
            financeManager.addExpense(date, amount, category, description);
            System.out.println("支出已成功记录！");
        } catch (IllegalArgumentException e) {
            System.out.println("错误：" + e.getMessage());
        }
    }

    private static void queryTransactions() {
        System.out.println("\n查询账单：");
        LocalDate startDate = getDateInput("起始日期（YYYY-MM-DD，留空表示不限）：");
        LocalDate endDate = getDateInput("结束日期（YYYY-MM-DD，留空表示不限）：");
        String category = getStringInput("类别（留空表示所有类别）：");
        financeManager.queryTransactions(startDate, endDate, category);
    }

    private static void setMonthlyBudget() {
        System.out.println("\n设置月度预算：");
        try {
            double budget = getDoubleInput("请输入月度预算金额：");
            financeManager.setMonthlyBudget(budget);
            System.out.println("月度预算设置成功！");
        } catch (IllegalArgumentException e) {
            System.out.println("错误：" + e.getMessage());
        }
    }

    private static void displayMonthlyStatistics() {
        System.out.println("\n查看月度统计报告：");
        int year = getIntInput("请输入年份：");
        int month = getIntInput("请输入月份（1-12）：");
        if (month < 1 || month > 12) {
            System.out.println("错误：无效的月份。");
            return;
        }
        financeManager.displayMonthlyStatistics(year, month);
    }

    private static LocalDate getDateInput(String prompt) {
        while (true) {
            try {
                String dateStr = getStringInput(prompt);
                if (dateStr.isEmpty()) {
                    return null;
                }
                return LocalDate.parse(dateStr, dateFormatter);
            } catch (DateTimeParseException e) {
                System.out.println("无效的日期格式。请使用YYYY-MM-DD格式或留空。");
            }
        }
    }

    private static double getDoubleInput(String prompt) {
        while (true) {
            try {
                System.out.print(prompt);
                return Double.parseDouble(scanner.nextLine());
            } catch (NumberFormatException e) {
                System.out.println("无效的输入。请输入一个数字。");
            }
        }
    }

    private static int getIntInput(String prompt) {
        while (true) {
            try {
                System.out.print(prompt);
                return Integer.parseInt(scanner.nextLine());
            } catch (NumberFormatException e) {
                System.out.println("无效的输入。请输入一个整数。");
            }
        }
    }

    private static String getStringInput(String prompt) {
        System.out.print(prompt);
        return scanner.nextLine().trim();
    }
}